/** @file process.c
 *
 * @author cameris camerisdump@gmail.com
 */

#include "process.h"
#include "vtparser.h"
#include "terminal.h"
#include "keyboard.h"
#include "keysymbols.h"
#include "profile.h"
#include "uart.h"
#include "lcd.h"
#include "font.h"
#include "bell.h"
#include <stdlib.h>

/** @name key sequences
 * @{ */ /* {{{ */
const __flash uint8_t F1_str[] = { SS3, 'P', '\0' };
const __flash uint8_t F2_str[] = { SS3, 'Q', '\0' };
const __flash uint8_t F3_str[] = { SS3, 'R', '\0' };
const __flash uint8_t F4_str[] = { SS3, 'S', '\0' };
const __flash uint8_t F5_str[] = { CSI, '1', '5', '~', '\0' };
const __flash uint8_t F6_str[] = { CSI, '1', '7', '~', '\0' };
const __flash uint8_t F7_str[] = { CSI, '1', '8', '~', '\0' };
const __flash uint8_t F8_str[] = { CSI, '1', '9', '~', '\0' };
const __flash uint8_t F9_str[] = { CSI, '2', '0', '~', '\0' };
const __flash uint8_t F10_str[] = { CSI, '2', '1', '~', '\0' };
const __flash uint8_t F11_str[] = { CSI, '2', '3', '~', '\0' };
const __flash uint8_t F12_str[] = { CSI, '2', '4', '~', '\0' };

const __flash uint8_t HOME_str[] = { CSI, '1', '~', '\0' };
const __flash uint8_t INSERT_str[] = { CSI, '2', '~', '\0' };
const __flash uint8_t DELETE_str[] = { CSI, '3', '~', '\0' };
const __flash uint8_t END_str[] = { CSI, '4', '~', '\0' };
const __flash uint8_t PGUP_str[] = { CSI, '5', '~', '\0' };
const __flash uint8_t PGDOWN_str[] = { CSI, '6', '~', '\0' };

const __flash uint8_t BACKTAB_str[] = { CSI, 'Z', '\0' };

const __flash uint8_t APP_0_str[] = { SS3, 'p', '\0' };
const __flash uint8_t APP_1_str[] = { SS3, 'q', '\0' };
const __flash uint8_t APP_2_str[] = { SS3, 'r', '\0' };
const __flash uint8_t APP_3_str[] = { SS3, 's', '\0' };
const __flash uint8_t APP_4_str[] = { SS3, 't', '\0' };
const __flash uint8_t APP_5_str[] = { SS3, 'u', '\0' };
const __flash uint8_t APP_6_str[] = { SS3, 'v', '\0' };
const __flash uint8_t APP_7_str[] = { SS3, 'w', '\0' };
const __flash uint8_t APP_8_str[] = { SS3, 'x', '\0' };
const __flash uint8_t APP_9_str[] = { SS3, 'y', '\0' };
const __flash uint8_t APP_DIVISION_str[] = { SS3, 'o', '\0' };
const __flash uint8_t APP_MULTIPLY_str[] = { SS3, 'j', '\0' };
const __flash uint8_t APP_MINUS_str[] = { SS3, 'm', '\0' };
const __flash uint8_t APP_PLUS_str[] = { SS3, 'k', '\0' };
const __flash uint8_t APP_COMMA_str[] = { SS3, 'l', '\0' };

const __flash uint8_t APP_LEFT_str[] = { SS3, 'D', '\0' };
const __flash uint8_t APP_DOWN_str[] = { SS3, 'B', '\0' };
const __flash uint8_t APP_RIGHT_str[] = { SS3, 'C', '\0' };
const __flash uint8_t APP_UP_str[] = { SS3, 'A', '\0' };
const __flash uint8_t APP_ENTER_str[] = { SS3, 'M', '\0' };

const __flash uint8_t LEFT_str[] = { CSI, 'D', '\0' };
const __flash uint8_t DOWN_str[] = { CSI, 'B', '\0' };
const __flash uint8_t RIGHT_str[] = { CSI, 'C', '\0' };
const __flash uint8_t UP_str[] = { CSI, 'A', '\0' };
/* @} */ /* }}} */

/** @brief process character
 * @param ch character */
static void process_char(uint8_t ch)
{ /* {{{ */
	switch (ch) {
		case XON:
			uart_start_receive();
			break;
		case XOFF:
			uart_stop_receive();
			break;
		default:
			uart_send_byte(ch);
			break;
	}

	if ( term_get_local_echo() ) {
		uart_put_recv_byte(ch);
	}
} /* }}} */

/** @brief process strings send by some keys
 * @param str pointer to flash string */
static void process_keystring(const __flash uint8_t *str)
{ /* {{{ */
	while (*str) {
		process_key(*str);
		str++;
	}
} /* }}} */

/** @brief callback for vt parser
 * @param parser pointer to the vtparser
 * @param action action to perform
 * @param ch final character */
void process_callback(struct vtparse *parser, uint8_t action, uint8_t ch)
{ /* {{{ */
	switch (action) {
		case VTPARSE_ACTION_PRINT:
			term_print(ch);
			break;

		case VTPARSE_ACTION_EXECUTE:
			process_execute(ch);
			break;

		case VTPARSE_ACTION_CSI_DISPATCH:
			process_csi(parser, ch);
			break;

		case VTPARSE_ACTION_ESC_DISPATCH:
			process_esc(parser, ch);
			break;

		case VTPARSE_ACTION_HOOK:
		case VTPARSE_ACTION_PUT:
		case VTPARSE_ACTION_OSC_START:
		case VTPARSE_ACTION_OSC_PUT:
		case VTPARSE_ACTION_OSC_END:
		case VTPARSE_ACTION_UNHOOK:
			/* not supported, ignore */
			break;
		default:
			break;
	}
} /* }}} */

/** @brief process C0 and C1 control characters
 * @param ch C0 or C1 character */
void process_execute(uint8_t ch)
{ /* {{{ */
	switch (ch) {
		case BEL: /* BEL - Bell {{{ */
			if (bell_get_enable()) {
				bell_add_tone(BELL_C6,10);
				bell_add_tone(BELL_NOSOUND,1);
			}
			break; /* }}} */
		case BS: /* BS - Backspace {{{ */
			term_move_cursor(-1, 0);
			break; /* }}} */
		case HT: /* HT - Horizontal Tab {{{ */
			term_tab_move(1);
			break; /* }}} */
		case LF: /* LF - Line Feed */
		case VT: /* VT - Vertical Tab */
		case FF: /* FF - Form Feed {{{ */
			term_scroll( term_move_cursor(0, 1) );
			break; /* }}} */
		case CR: /* CR - Carriage Return {{{ */
			term_move_cursor(-1* term_get_cursor_col(), 0);
			break; /* }}} */
		case SO: /* SO (LS1) - Shift Out (Locking Shift 1) {{{ */
			font_set_active(GL, G1);
			break; /* }}} */
		case SI: /* SI (LS0) - Shift In (Locking Shift 0) {{{ */
			font_set_active(GL, G0);
			break; /* }}} */
		/*case XON: [> XON <]*/
		/*case XOFF: [> XOFF {{{ <]*/
			/*[> handeled by uart.c and process_key <]*/
			/*break; [> }}} <]*/
		/*case SUB: [> SUB - Substitute {{{ <]*/
			/*break; [> }}} <]*/
		case IND: /* IND - Index {{{ */
			term_scroll( term_move_cursor(0, 1) );
			break; /* }}} */
		case NEL: /* NEL - Next Line {{{ */
			term_scroll( term_move_cursor(-1* term_get_cursor_col(), 1) );
			break; /* }}} */
		case HTS: /* HTS - Horizontal Tab Set {{{ */
			term_tab_set( term_get_cursor_col() );
			break; /* }}} */
		case RI: /* RI - inverse Index {{{ */
			term_scroll( term_move_cursor(0, -1) );
			break; /* }}} */
		case SS2: /* SS2 - Single Shift 2 {{{ */
			font_set_singleshift(G2);
			break; /* }}} */
		case SS3: /* SS3 - Single Shift 3 {{{ */
			font_set_singleshift(G3);
			break; /* }}} */
		/*case CCH: [> CCH - Cancel Character {{{ <]*/
			/*break; [> }}} <]*/
	}
} /* }}} */

/** @brief process escape sequences
 * @param parser the parser
 * @param ch final character of escape sequence */
void process_esc(struct vtparse *parser, uint8_t ch)
{ /* {{{ */
	switch (ch) {
		case '0':
			/* SCS - DEC special graphics {{{ */
			if (parser->intermediate_chars[0] == '(') {
				font_set_selected(G0, FONT_GRAPHIC);
			} else if (parser->intermediate_chars[0] == ')') {
				font_set_selected(G1, FONT_GRAPHIC);
			} else if (parser->intermediate_chars[0] == '*') {
				font_set_selected(G2, FONT_GRAPHIC);
			} else if (parser->intermediate_chars[0] == '+') {
				font_set_selected(G3, FONT_GRAPHIC);
			}
			break; /* }}} */
		case '7': /* DECSC - Save Cursor {{{ */
			term_save_cursor();
			break; /* }}} */
		case '8': /* DECRC - Restore Cursor {{{ */
			term_restore_cursor();
			break; /* }}} */
		case 'A':
			/* SCS - LATIN1 {{{ */
			if (parser->intermediate_chars[0] == '-') {
				font_set_selected(G1, FONT_LATIN1);
			} else if (parser->intermediate_chars[0] == '.') {
				font_set_selected(G2, FONT_LATIN1);
			} else if (parser->intermediate_chars[0] == '/') {
				font_set_selected(G3, FONT_LATIN1);
			}
			break; /* }}} */
		case 'B':
			/* SCS - ASCII {{{ */
			if (parser->intermediate_chars[0] == '(') {
				font_set_selected(G0, FONT_ASCII);
			} else if (parser->intermediate_chars[0] == ')') {
				font_set_selected(G1, FONT_ASCII);
			} else if (parser->intermediate_chars[0] == '*') {
				font_set_selected(G2, FONT_ASCII);
			} else if (parser->intermediate_chars[0] == '+') {
				font_set_selected(G3, FONT_ASCII);
			}
			break; /* }}} */
		case 'c': /* RIS - Reset to Initial State {{{ */
			uart_start_receive();
			kbd_init_after_sei();
			term_init();
			break; /* }}} */
		case 'D': /* IND - Index {{{ */
			term_scroll( term_move_cursor(0, 1) );
			break; /* }}} */
		case 'M': /* RI - inverse Index {{{ */
			term_scroll( term_move_cursor(0, -1) );
			break; /* }}} */
		case 'E': /* NEL - Next Line {{{ */
			term_scroll( term_move_cursor(-1* term_get_cursor_col(), 1) );
			break; /* }}} */
		case 'H': /* HTS - Horizontal Tab Set {{{ */
			term_tab_set( term_get_cursor_col() );
			break; /* }}} */
		case '>': /* DECKPNM - Keypad Numeric Modes {{{ */
			term_set_kp_application(0);
			break; /* }}} */
		case '=': /* DECKPAM - Keypad Applications Modes {{{ */
			term_set_kp_application(1);
			break; /* }}} */
		case '~': /* LS1R - Locking Shift 1 Right {{{ */
			font_set_active(GR, G1);
			break; /* }}} */
		case 'n': /* LS2 - Locking Shift 2 {{{ */
			font_set_active(GL, G2);
			break; /* }}} */
		case '}': /* LS2R - Locking Shift 2 Right {{{ */
			font_set_active(GR, G2);
			break; /* }}} */
		case 'o': /* LS3 - Locking Shift 3 {{{ */
			font_set_active(GL, G3);
			break; /* }}} */
		case '|': /* LS3R - Locking Shift 3 Right {{{ */
			font_set_active(GR, G3);
			break; /* }}} */
		case 'N': /* SS2 - Single Shift 2 {{{ */
			font_set_singleshift(G2);
			break; /* }}} */
		case 'O': /* SS3 - Single Shift 3 {{{ */
			font_set_singleshift(G3);
			break; /* }}} */
		case 'F':
			/* S7C1T - Send 7bit C1 Control Character to Host {{{ */
			if (parser->intermediate_chars[0] == ' ') {
				term_set_sendc1(0);
			}
			break; /* }}} */
		case 'G':
			/* S8C1T - Send 8bit C1 Control Character to Host {{{ */
			if (parser->intermediate_chars[0] == ' ') {
				term_set_sendc1(1);
			}
			break; /* }}} */
	}
} /* }}} */

/** @brief process csi sequences
 * @param parser the parser
 * @param ch final character of csi sequence */
void process_csi(struct vtparse *parser, uint8_t ch)
{ /* {{{ */
	char tmp[5];
	char *pnt;
	switch (ch) {
		case 'A': /* CUU - Cursor Up {{{ */
			if (parser->num_params < 1 || parser->params[0] == 0) {
				parser->params[0] = 1;
			}
			term_move_cursor(0, -1* parser->params[0]);
			break; /* }}} */
		case 'B': /* CUD - Cursor Down {{{ */
			if (parser->num_params < 1 || parser->params[0] == 0) {
				parser->params[0] = 1;
			}
			term_move_cursor(0, parser->params[0]);
			break; /* }}} */
		case 'a': /* HPR - Horizontal Position Relative (since no left/right
					 margin suppported) */
		case 'C': /* CUF - Cursor Forward {{{ */
			if (parser->num_params < 1 || parser->params[0] == 0) {
				parser->params[0] = 1;
			}
			term_move_cursor(parser->params[0], 0);
			break; /* }}} */
		case 'D': /* CUB - Cursor Backward {{{ */
			if (parser->num_params < 1 || parser->params[0] == 0) {
				parser->params[0] = 1;
			}
			term_move_cursor(-1* parser->params[0], 0);
			break; /* }}} */
		case 'H': /* CUP - Cursor Position */
		case 'f': /* HVP - Horizontal and Vertical Position {{{ */
			if (parser->num_params < 1 || parser->params[0] == 0) {
				parser->params[0] = 1;
			}
			if (parser->num_params < 2 || parser->params[1] == 0) {
				parser->params[1] = 1;
			}

			if (term_get_originmode()) {
				parser->params[0] += -1 + term_get_top_margin();
				if (parser->params[0] > term_get_bottom_margin())
					parser->params[0] = term_get_bottom_margin();
				term_set_cursor_position(parser->params[1] -1, parser->params[0]);
			} else {
				term_set_cursor_position(parser->params[1] -1, parser->params[0] -1);
			}
			break; /* }}} */
		case 'G': /* CHA - Cursor Horizontal Absolute */
		case '`': /* HPA - Horizontal Position Absolute {{{ */
			if (parser->num_params < 1 || parser->params[0] == 0) {
				parser->params[0] = 1;
			}
			term_set_cursor_position(parser->params[0] -1, term_get_cursor_line());
			break; /* }}} */
		case 'd': /* VPA - Vertical Line Position Absolute {{{ */
			if (parser->num_params < 1 || parser->params[0] == 0) {
				parser->params[0] = 1;
			}
			term_set_cursor_position(term_get_cursor_col(), parser->params[0] -1);
			break; /* }}} */
		case 'e': /* VPR - Vertical Position Relative {{{ */
			if (parser->num_params < 1 || parser->params[0] == 0) {
				parser->params[0] = 1;
			}
			term_set_cursor_position(term_get_cursor_col(),
					term_get_cursor_line() + parser->params[0] -1);
			break; /* }}} */
		case 'I': /* CHT - Cursor Horizontal Forward Tabulation {{{ */
			if (parser->num_params < 1 || parser->params[0] == 0) {
				parser->params[0] = 1;
			}
			term_tab_move(parser->params[0]);
			break; /* }}} */
		case 'Z': /* CBT - Cursor Backward Tabulation {{{ */
			if (parser->num_params < 1 || parser->params[0] == 0) {
				parser->params[0] = 1;
			}
			term_tab_move(-1* parser->params[0]);
			break; /* }}} */
		case 'E': /* CNL - Cursor Next Line {{{ */
			if (parser->num_params < 1 || parser->params[0] == 0) {
				parser->params[0] = 1;
			}
			term_move_cursor(-1* term_get_cursor_col(), parser->params[0]);
			break; /* }}} */
		case 'F': /* CPL - Cursor Previous Line {{{ */
			if (parser->num_params < 1 || parser->params[0] == 0) {
				parser->params[0] = 1;
			}
			term_move_cursor(-1* term_get_cursor_col(), -1*parser->params[0]);
			break; /* }}} */
		case 'n': /* DSR - Device Status Reports {{{ */
			if (parser->num_params < 1) return;
			switch (parser->params[0]) {
				case 6: /* CPR - Cursor Position Report {{{ */
					//TODO make send keystring universal for string and flash string
					process_key(CSI);
					utoa(term_get_cursor_line()+1, tmp, 10);
					pnt = tmp;
					while (*pnt) {
						process_char(*pnt);
						pnt++;
					}
					process_char(';');
					utoa(term_get_cursor_col()+1, tmp, 10);
					pnt = tmp;
					while (*pnt) {
						process_char(*pnt);
						pnt++;
					}
					process_char('R');
					break;
			} /* }}} */
			break; /* }}} */
		case 'g': /* TBC - Clear Tabs {{{ */
			if (parser->num_params < 1)  {
				parser->params[0] = 0;
			}
			term_tab_clear(parser->params[0]);
			break; /* }}} */
		case 'r': /* DECSTBM - Set Top and Bottom Margins {{{ */
			if (parser->num_params < 1 || parser->params[0] == 0) {
				parser->params[0] = 1;
			}
			if (parser->num_params < 2 || parser->params[1] == 0) {
				parser->params[1] = term_get_last_line() + 1;
			}
			term_set_margins(parser->params[0] -1, parser->params[1] -1);
			term_set_cursor_position(0, 0);
			break; /* }}} */
		case 'P': /* DCH - Delete Character {{{ */
			if (parser->num_params < 1 || parser->params[0] == 0) {
				parser->params[0] = 1;
			}
			term_del_char(parser->params[0]);
			break; /* }}} */
		case 'X': /* ECH - Erase Character {{{ */
			if (parser->num_params < 1 || parser->params[0] == 0) {
				parser->params[0] = 1;
			}
			term_erase_char(parser->params[0]);
			break; /* }}} */
		case '@': /* ICH - Inser Character {{{ */
			if (parser->num_params < 1 || parser->params[0] == 0) {
				parser->params[0] = 1;
			}
			term_ins_char(parser->params[0]);
			break; /* }}} */
		case 'M': /* DL - Delete Line {{{ */
			if (parser->num_params < 1 || parser->params[0] == 0) {
				parser->params[0] = 1;
			}
			term_ins_or_del_line(-1*parser->params[0]);
			break; /* }}} */
		case 'K': /* EL - Erase in Line {{{ */
			if (parser->num_params < 1)  {
				parser->params[0] = 0;
			}
			term_erase_line(parser->params[0]);
			break; /* }}} */
		case 'L': /* IL - Insert Line {{{ */
			if (parser->num_params < 1 || parser->params[0] == 0) {
				parser->params[0] = 1;
			}
			term_ins_or_del_line(parser->params[0]);
			break; /* }}} */
		case 'J': /* ED - Erase in Display {{{ */
			if (parser->num_params < 1)  {
				parser->params[0] = 0;
			}
			term_erase_display(parser->params[0]);
			break; /* }}} */
		case 'p':
			/* DECSTR - Soft Terminal Reset {{{ */
			if (parser->intermediate_chars[0] == '!') {
				kbd_init_after_sei();
				term_reset();
			}
			break; /* }}} */
		case '~':
			/* DECPS - Play Sound {{{ */
			if (parser->intermediate_chars[0] == ',') {
				if (parser->num_params >= 2) {
					bell_add_tone(parser->params[1],parser->params[0]);
				}
			}
			break; /* }}} */
		case 't':
			/* DECSWBV - Set Warning Bell Volume (just enable/disable) {{{ */
			if (parser->intermediate_chars[0] == ' ') {
				if (parser->num_params < 1) {
					parser->params[0] = 0;
				}
				if (parser->params[0] == 0 || (parser->params[0] >= 2 &&
							parser->params[0] <= 8)) {
					bell_enable(1);
				} else if (parser->params[0] == 1) {
					bell_enable(0);
				}
			}
			break; /* }}} */
		case 'W':
			/* DECST8C - Set Tab at Every 8 Columns {{{ */
			if (parser->num_params > 0 && parser->params[0] == 5 &&
					parser->intermediate_chars[0] == '?') {
				term_tab_clear(3);
				for (uint8_t t=8; t <= term_get_last_col(); t += 8) {
					term_tab_set(t);
				}
			}
			break; /* }}} */
		case 'm': /* SGR - Select Graphic Rendition {{{ */
			if (parser->num_params < 1)  {
				parser->params[0] = 0;
				parser->num_params = 1;
			}
			for (uint8_t i=0; i<parser->num_params; i++) {

				/* set fg color */
				if (parser->params[i] >= 30 && parser->params[i] <= 37) {
					/* setting a color while bold is set uses light colors */
					if ( term_get_char_bold() ) {
						term_set_fgcolor(parser->params[i] -30+8);
					} else {
						term_set_fgcolor(parser->params[i] -30);
					}
				}
				/* set bg color */
				if (parser->params[i] >= 40 && parser->params[i] <= 47) {
					term_set_bgcolor(parser->params[i] -40);
				}
				/* set fg color bright */
				if (parser->params[i] >= 90 && parser->params[i] <= 97) {
					term_set_fgcolor(parser->params[i] -90+8);
				}
				/* set bg color bright */
				if (parser->params[i] >= 100 && parser->params[i] <= 107) {
					term_set_bgcolor(parser->params[i] -100+8);
				}

				switch(parser->params[i]) {
					case 0: /* all attributes off */
						term_reset_char_attr();
						term_reset_fgcolor();
						term_reset_bgcolor();
						break;
					case 1: /* bold */
						term_set_char_bold(1);
						if (term_get_fgcolor() >= 0 && term_get_fgcolor() <= 7) {
							term_set_fgcolor(term_get_fgcolor()+8);
						}
						break;
					case 4: /* underline */
						term_set_char_underline(1);
						break;
					case 5: /* blinking */
						term_set_char_blink(1);
						break;
					case 7: /* inverse */
						term_set_char_inverse(1);
						break;
					case 22: /* bold off */
						term_set_char_bold(0);
						/* resetting light colors to darker ones, see "set fg color" */
						if (term_get_fgcolor() <= 15 && term_get_fgcolor() >= 8) {
							term_set_fgcolor(term_get_fgcolor()-8);
						}
						break;
					case 24: /* underline off */
						term_set_char_underline(0);
						break;
					case 25: /* blinking off */
						term_set_char_blink(0);
						break;
					case 27: /* inverse off */
						term_set_char_inverse(0);
						break;
					case 38:
						/* set fg color (256) */
						if (i+2 < parser->num_params && parser->params[i+1] == 5) {
							i += 2;
							term_set_fgcolor(parser->params[i]);
						}
						break;
					case 39: /* default fg color */
						term_reset_fgcolor();
						break;
					case 48:
						/* set bg color (256) */
						if (i+2 < parser->num_params && parser->params[i+1] == 5) {
							i += 2;
							term_set_bgcolor(parser->params[i]);
						}
						break;
					case 49: /* default bg color */
						term_reset_bgcolor();
						break;
				}
			}
			break; /* }}} */
		case 'h': /* SM - Set Mode {{{ */
			/* DEC private modes */
			if (parser->intermediate_chars[0] == '?') {
				for (uint8_t i=0; i<parser->num_params; i++) {
					switch(parser->params[i]) {
						case 1: /* DECCKM - Cursor Keys Mode */
							term_set_cursorkeymode(1);
							break;
						case 5: /* DECSCNM - Screen Mode: Light or Dark Screen */
							term_set_screenmode(1);
							break;
						case 6: /* DECOM - Origin Mode */
							term_set_originmode(1);
							break;
						case 7: /* DECAWM - Autowrap Mode */
							term_set_autowrap(1);
							break;
						case 25: /* DECTCEM - Text Cursor Enable Mode */
							term_set_cursor_visible(1);
							break;
						case 66: /* DECNKM - Numeric Keypad Mode */
							term_set_kp_application(1);
							break;
						case 67: /* DECBKM - Backarrow Key Mode */
							term_set_backarrowmode(1);
							break;
						case 108: /* DECNUMLK - Num Lock Mode */
							kbd_set_numlock(1);
							break;
						case 109: /* DECCAPSLK - Caps Lock Mode */
							kbd_set_capslock(1);
							break;
					}
				}
			/* ANSI modes */
			} else {
				for (uint8_t i=0; i<parser->num_params; i++) {
					switch(parser->params[i]) {
						case 2: /* KAM - Keyboard Action Mode */
							kbd_disable();
							break;
						case 3: /* CRM - Show Control Character Mode */
							term_set_show_control_chars(1);
							break;
						case 4: /* IRM - Insert/Replace Mode */
							term_set_insertmode(1);
							break;
						case 12: /* SRM - Local Echo: Send/Receive Mode */
							term_set_local_echo(0);
							break;
						case 20: /* LNM - Line Feed/New Line Mode */
							term_set_newlinemode(1);
							break;
					}
				}
			}
			break; /* }}} */
		case 'l': /* RM - reset Mode {{{ */
			/* DEC private modes */
			if (parser->intermediate_chars[0] == '?') {
				for (uint8_t i=0; i<parser->num_params; i++) {
					switch(parser->params[i]) {
						case 1: /* DECCKM - Cursor Keys Mode */
							term_set_cursorkeymode(0);
							break;
						case 5: /* DECSCNM - Screen Mode: Light or Dark Screen */
							term_set_screenmode(0);
							break;
						case 6: /* DECOM - Origin Mode */
							term_set_originmode(0);
							break;
						case 7: /* DECAWM - Autowrap Mode */
							term_set_autowrap(0);
							break;
						case 25: /* DECTCEM - Text Cursor Enable Mode */
							term_set_cursor_visible(0);
							break;
						case 66: /* DECNKM - Numeric Keypad Mode */
							term_set_kp_application(0);
							break;
						case 67: /* DECBKM - Backarrow Key Mode */
							term_set_backarrowmode(0);
							break;
						case 108: /* DECNUMLK - Num Lock Mode */
							kbd_set_numlock(0);
							break;
						case 109: /* DECCAPSLK - Caps Lock Mode */
							kbd_set_capslock(0);
							break;
					}
				}
			/* ANSI modes */
			} else {
				for (uint8_t i=0; i<parser->num_params; i++) {
					switch(parser->params[i]) {
						case 2: /* KAM - Keyboard Action Mode */
							kbd_enable();
							break;
						case 3: /* CRM - Show Control Character Mode */
							term_set_show_control_chars(0);
							break;
						case 4: /* IRM - Insert/Replace Mode */
							term_set_insertmode(0);
							break;
						case 12: /* SRM - Local Echo: Send/Receive Mode */
							term_set_local_echo(1);
							break;
						case 20: /* LNM - Line Feed/New Line Mode */
							term_set_newlinemode(0);
							break;
					}
				}
			}
			break; /* }}} */
	}
} /* }}} */

/** @brief process character if in show control mode
 * @param ch character */
void process_show_control_chars(uint8_t ch)
{ /* {{{ */
	term_print(ch);
	if (ch == LF || ch == VT  || ch == FF ) {
		term_scroll(term_move_cursor(-1* term_get_cursor_col(),1));
	}
} /* }}} */

/** @brief process a keyboard key
 * @param key keyboard symbol */
void process_key(uint16_t key)
{ /* {{{ */
	uint8_t key8;

	if (key == NO_KEY) {
		return;
	} else if (key > 0xFF) { /* handle special key (F1,INSERT, ...) */
		switch(key) {
			/* setup keys {{{ */
			case BLIGHTINC:
				curprofile.backlight = lcd_set_relative_bl(1);
				break;
			case BLIGHTDEC:
				curprofile.backlight = lcd_set_relative_bl(-1);
				break;
			case BAUDINC:
				uart_inc_baud(&curprofile.uart);
				profile_print_status();
				break;
			case BAUDDEC:
				uart_dec_baud(&curprofile.uart);
				profile_print_status();
				break;
			case DATABITNEXT:
				uart_next_databits(&curprofile.uart);
				profile_print_status();
				break;
			case PARITYNEXT:
				uart_next_parity(&curprofile.uart);
				profile_print_status();
				break;
			case STOPBITTOG:
				uart_toggle_stopbits(&curprofile.uart);
				profile_print_status();
				break;
			case LOCALECHOTOG:
				curprofile.localecho ^= 1;
				term_set_local_echo(curprofile.localecho);
				profile_print_status();
				break;
			case AUTOWRAPTOG:
				curprofile.autowrap ^= 1;
				term_set_autowrap(curprofile.autowrap);
				profile_print_status();
				break;
			case SHOWCONTROLTOG:
				curprofile.showcontrol ^= 1;
				term_set_show_control_chars(curprofile.showcontrol);
				profile_print_status();
				break;
			case NEWLINETOG:
				curprofile.newline ^= 1;
				term_set_newlinemode(curprofile.newline);
				profile_print_status();
				break;
			case SCREENTOG:
				curprofile.screen ^= 1;
				term_set_screenmode(curprofile.screen);
				break;
			case BELLTOG:
				curprofile.bell ^= 1;
				bell_enable(curprofile.bell);
				profile_print_status();
				break;
			case SHOWPROFILE:
				profile_print_status();
				break;
			case LOADPROFILE1:
				profile_load(0);
				profile_init();
				break;
			case LOADPROFILE2:
				profile_load(1);
				profile_init();
				break;
			case LOADPROFILE3:
				profile_load(2);
				profile_init();
				break;
			case SAVEPROFILE1:
				profile_save(0);
				break;
			case SAVEPROFILE2:
				profile_save(1);
				break;
			case SAVEPROFILE3:
				profile_save(2);
				break; /* }}} */
			/* named keys {{{ */
			case F1:
				process_keystring(F1_str);
				break;
			case F2:
				process_keystring(F2_str); 
				break;
			case F3:
				process_keystring(F3_str); 
				break;
			case F4:
				process_keystring(F4_str); 
				break;
			case F5:
				process_keystring(F5_str); 
				break;
			case F6:
				process_keystring(F6_str); 
				break;
			case F7:
				process_keystring(F7_str); 
				break;
			case F8:
				process_keystring(F8_str); 
				break;
			case F9:
				process_keystring(F9_str); 
				break;
			case F10:
				process_keystring(F10_str); 
				break;
			case F11:
				process_keystring(F11_str); 
				break;
			case F12:
				process_keystring(F12_str); 
				break;
			case INSERT:
				process_keystring(INSERT_str);
				break;
			case PGUP:
				process_keystring(PGUP_str);
				break;
			case PGDOWN:
				process_keystring(PGDOWN_str);
				break;
			case END:
				process_keystring(END_str);
				break;
			case HOME:
				process_keystring(HOME_str);
				break;
			case DELETE:
				process_keystring(DELETE_str);
				break;
			case UP:
				if (term_get_cursorkeymode()) process_keystring(APP_UP_str);
				else process_keystring(UP_str);
				break;
			case LEFT:
				if (term_get_cursorkeymode()) process_keystring(APP_LEFT_str);
				else process_keystring(LEFT_str);
				break;
			case DOWN:
				if (term_get_cursorkeymode()) process_keystring(APP_DOWN_str);
				else process_keystring(DOWN_str);
				break;
			case RIGHT:
				if (term_get_cursorkeymode()) process_keystring(APP_RIGHT_str);
				else process_keystring(RIGHT_str);
				break;
			case ENTER:
				process_char(CR);
				if (term_get_newlinemode()) process_char(LF);
				break;
			case BACKSPACE:
				if (term_get_backarrowmode()) process_char(BS);
				else process_char(DEL);
				break; /* }}} */
			/* keypad keys {{{ */
			case KP_0:
				if (term_get_kp_application()) process_keystring(APP_0_str);
				else process_keystring(INSERT_str);
				break;
			case KP_1:
				if (term_get_kp_application()) process_keystring(APP_1_str);
				else process_keystring(END_str);
				break;
			case KP_2:
				if (term_get_kp_application()) process_keystring(APP_2_str);
				else process_key(DOWN);
				break;
			case KP_3:
				if (term_get_kp_application()) process_keystring(APP_3_str);
				else process_keystring(PGDOWN_str);
				break;
			case KP_4:
				if (term_get_kp_application()) process_keystring(APP_4_str);
				else process_key(LEFT);
				break;
			case KP_5:
				if (term_get_kp_application()) process_keystring(APP_5_str);
				break;
			case KP_6:
				if (term_get_kp_application()) process_keystring(APP_6_str);
				else process_key(RIGHT);
				break;
			case KP_7:
				if (term_get_kp_application()) process_keystring(APP_7_str);
				else process_keystring(HOME_str);
				break;
			case KP_8:
				if (term_get_kp_application()) process_keystring(APP_8_str);
				else process_key(UP);
				break;
			case KP_9:
				if (term_get_kp_application()) process_keystring(APP_9_str);
				else process_keystring(PGUP_str);
				break;
			case KP_COMMA:
				if (term_get_kp_application()) process_keystring(APP_COMMA_str);
				else process_keystring(DELETE_str);
				break;
			case KP_ENTER:
				if (term_get_kp_application()) process_keystring(APP_ENTER_str);
				else process_key(ENTER);
				break;
			case KP_PLUS:
				if (term_get_kp_application()) process_keystring(APP_PLUS_str);
				else process_char('+');
				break;
			case KP_MINUS:
				if (term_get_kp_application()) process_keystring(APP_MINUS_str);
				else process_char('-');
				break;
			case KP_MULTIPLY:
				if (term_get_kp_application()) process_keystring(APP_MULTIPLY_str);
				else process_char('*');
				break;
			case KP_DIVISION:
				if (term_get_kp_application()) process_keystring(APP_DIVISION_str);
				else process_char('/');
				break; /* }}} */
			/* other keys {{{ */
			case BACKTAB:
				process_keystring(BACKTAB_str);
				break; /* }}} */
		}
	} else { /* control and printable characters */
		key8 = (uint8_t) key;
		if ((!term_get_sendc1()) && 0x80 <= key8 && 0x9F >= key8) { /* C1 control character */
			process_char(ESC);
			switch(key8) {
				case CSI:
					key8 = '[';
					break;
				case SS3:
					key8 = 'O';
					break;
			}
		}
		process_char(key8);
	}
} /* }}} */

/* vim:set foldmethod=marker: */
